{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 7、卷积神经网络"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "本章的主题是卷积神经网络（Convolutional Neural Network， CNN）。  \n",
    "CNN被用于图像识别、语音识别等各种场合，在图像识别的比赛中，基于深度学习的方法几乎都以CNN为基础。  \n",
    "本章将详细介绍CNN的结构，并用Python实现其处理内容。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.1、整体结构"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先，来看一下CNN的网络结构，了解CNN的大致框架。   \n",
    "CNN和之前介绍的神经网络一样，可以像乐高积木一样通过组装层来构建。  \n",
    "不过，CNN中新出现了卷积层（Convolution层）和池化层（Pooling层）。  \n",
    "卷积层和池化层将在下一节详细介绍，这里我们先看一下如何组装层以构建CNN。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "之前介绍的神经网络中，相邻层的所有神经元之间都有连接，这称为**全连接**（fully-connected）。  \n",
    "另外，我们用Affine层实现了全连接层。  \n",
    "如果使用这个Affine层，一个5层的全连接的神经网络就可以通过图7-1所示的网络结构来实现。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如图7-1所示，全连接的神经网络中， Affine层后面跟着激活函数ReLU层（或者Sigmoid层）。  \n",
    "这里堆叠了4层“Affine-ReLU”组合，然后第5层是Affine层，最后由Softmax层输出最终结果（概率）。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_1.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "那么， CNN会是什么样的结构呢？图7-2是CNN的一个例子。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_2.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如 图 7-2 所 示， CNN 中 新 增 了 Convolution 层 和 Pooling 层。   \n",
    "CNN 的层的连接顺序是“Convolution - ReLU -（Pooling）”（Pooling层有时会被省略）。  \n",
    "这可以理解为之前的“Affine - ReLU”连接被替换成了“Convolution - ReLU -（Pooling）”连接。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "还需要注意的是，在图7-2的CNN中，靠近输出的层中使用了之前的“Affine - ReLU”组合。  \n",
    "此外，最后的输出层中使用了之前的“Affine - Softmax”组合。  \n",
    "这些都是一般的CNN中比较常见的结构。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.2、卷积层"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "CNN中出现了一些特有的术语，比如填充、步幅等。  \n",
    "此外，各层中传递的数据是有形状的数据（比如， 3维数据），这与之前的全连接网络不同，因此刚开始学习CNN时可能会感到难以理解。  \n",
    "本节我们将花点时间，认真学习一下CNN中使用的卷积层的结构。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.2.1、全连接层存在的问题"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "之前介绍的全连接的神经网络中使用了全连接层（Affine层）。  \n",
    "在全连接层中，相邻层的神经元全部连接在一起，输出的数量可以任意决定。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "全连接层存在什么问题呢？  \n",
    "那就是数据的形状被“忽视”了。  \n",
    "比如，输入数据是图像时，图像通常是高、长、通道方向上的3维形状。  \n",
    "但是，向全连接层输入时，需要将3维数据拉平为1维数据。  \n",
    "实际上，前面提到的使用了MNIST数据集的例子中，输入图像就是1通道、高28像素、长28像素的（1, 28, 28）形状，但却被排成1列，以784个数据的形式输入到最开始的Affine层。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图像是3维形状，这个形状中应该含有重要的空间信息。  \n",
    "比如，空间上邻近的像素为相似的值、 RBG的各个通道之间分别有密切的关联性、相距较远的像素之间没有什么关联等， 3维形状中可能隐藏有值得提取的本质模式。  \n",
    "但是，因为全连接层会忽视形状，将全部的输入数据作为相同的神经元（同一维度的神经元）处理，所以无法利用与形状相关的信息。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "而卷积层可以保持形状不变。  \n",
    "当输入数据是图像时，卷积层会以3维数据的形式接收输入数据，并同样以3维数据的形式输出至下一层。  \n",
    "因此，在CNN中，可以（有可能）正确理解图像等具有形状的数据。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "另外， CNN 中，有时将卷积层的输入输出数据称为**特征图**（feature map）。  \n",
    "其中，卷积层的输入数据称为**输入特征图**（input feature map），输出数据称为**输出特征图**（output feature map）。  \n",
    "本书中将“输入输出数据”和“特征图”作为含义相同的词使用。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.2.2、卷积运算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "卷积层进行的处理就是卷积运算。  \n",
    "卷积运算相当于图像处理中的“滤波器运算”。在介绍卷积运算时，我们来看一个具体的例子（图7-3）。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_3.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如图7-3所示，卷积运算对输入数据应用滤波器。  \n",
    "在这个例子中，输入数据是有高长方向的形状的数据，滤波器也一样，有高长方向上的维度。  \n",
    "假设用（height, width）表示数据和滤波器的形状，则在本例中，输入大小是(4, 4)，滤波器大小是(3, 3)，输出大小是(2, 2)。  \n",
    "另外，有的文献中也会用“核”这个词来表示这里所说的“滤波器”。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在来解释一下图7-3的卷积运算的例子中都进行了什么样的计算。  \n",
    "图7-4中展示了卷积运算的计算顺序。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于输入数据，卷积运算以一定间隔滑动滤波器的窗口并应用。  \n",
    "这里所说的窗口是指图7-4中灰色的3 × 3的部分。  \n",
    "如图7-4所示，将各个位置上滤波器的元素和输入的对应元素相乘，然后再求和（有时将这个计算称为乘积累加运算）。  \n",
    "然后，将这个结果保存到输出的对应位置。  \n",
    "将这个过程在所有位置都进行一遍，就可以得到卷积运算的输出。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_4.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在全连接的神经网络中，除了权重参数，还存在偏置。  \n",
    "CNN中，滤波器的参数就对应之前的权重。  \n",
    "并且， CNN中也存在偏置。图7-3的卷积运算的例子一直展示到了应用滤波器的阶段。  \n",
    "包含偏置的卷积运算的处理流如图7-5所示。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如图7-5所示，向应用了滤波器的数据加上了偏置。  \n",
    "偏置通常只有1个（1 × 1）（本例中，相对于应用了滤波器的4个数据，偏置只有1个），这个值会被加到应用了滤波器的所有元素上。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_5.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.2.3、填充"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在进行卷积层的处理之前，有时要向输入数据的周围填入固定的数据（比如0等），这称为**填充**（padding），是卷积运算中经常会用到的处理。  \n",
    "比如，在图7-6的例子中，对大小为(4, 4)的输入数据应用了幅度为1的填充。  \n",
    "“幅度为1的填充”是指用幅度为1像素的0填充周围。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_6.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如图7-6所示，通过填充，大小为(4, 4)的输入数据变成了(6, 6)的形状。  \n",
    "然后，应用大小为(3, 3)的滤波器，生成了大小为(4, 4)的输出数据。  \n",
    "这个例子中将填充设成了1，不过填充的值也可以设置成2、 3等任意的整数。  \n",
    "在图7-5的例子中，如果将填充设为2，则输入数据的大小变为(8, 8)；如果将填充设为3，则大小变为(10, 10)。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用填充主要是为了调整输出的大小。  \n",
    "比如，对大小为(4, 4)的输入数据应用(3, 3)的滤波器时，输出大小变为(2, 2)，相当于输出大小\n",
    "比输入大小缩小了2个元素。  \n",
    "这在反复进行多次卷积运算的深度网络中会成为问题。  \n",
    "为什么呢？因为如果每次进行卷积运算都会缩小空间，那么在某个时刻输出大小就有可能变为 1，导致无法再应用卷积运算。  \n",
    "为了避免出现这样的情况，就要使用填充。  \n",
    "在刚才的例子中，将填充的幅度设为1，那么相对于输入大小(4, 4)，输出大小也保持为原来的(4, 4)。  \n",
    "因此，卷积运算就可以在保持空间大小不变的情况下将数据传给下一层。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.2.4、步幅"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "应用滤波器的位置间隔称为**步幅**（stride）。  \n",
    "之前的例子中步幅都是1，如果将步幅设为2，则如图7-7所示，应用滤波器的窗口的间隔变为2个元素。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_7.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在图7-7的例子中，对输入大小为(7, 7)的数据，以步幅2应用了滤波器。  \n",
    "通过将步幅设为2，输出大小变为(3, 3)。  \n",
    "像这样，步幅可以指定应用滤波器的间隔。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "综上，增大步幅后，输出大小会变小。  \n",
    "而增大填充后，输出大小会变大。  \n",
    "如果将这样的关系写成算式，会如何呢？  \n",
    "接下来，我们看一下对于填充和步幅，如何计算输出大小。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里，假设输入大小为(H, W)，滤波器大小为(FH, FW)，输出大小为(OH, OW)，填充为P，步幅为S。  \n",
    "此时，输出大小可通过式(7.1)进行计算。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "\\begin{array} { l } { O H = \\frac { H + 2 P - F H } { S } + 1 } \\\\ { O W = \\frac { W + 2 P - F W } { S } + 1 } \\end{array}\n",
    "$$\n",
    "<center>式（7.1）</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，我们使用这个算式，试着做几个计算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "例1：图7-6的例子"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "输入大小： (4, 4)；填充： 1；步幅： 1；滤波器大小： (3, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "\\begin{aligned} O H & = \\frac { 4 + 2 \\cdot 1 - 3 } { 1 } + 1 = 4 \\\\ O W & = \\frac { 4 + 2 \\cdot 1 - 3 } { 1 } + 1 = 4 \\end{aligned}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "例2：图7-7的例子"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "输入大小： (7, 7)；填充： 0；步幅： 2；滤波器大小： (3, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "\\begin{array} { l } { O H = \\frac { 7 + 2 \\cdot 0 - 3 } { 2 } + 1 = 3 } \\\\ { O W = \\frac { 7 + 2 \\cdot 0 - 3 } { 2 } + 1 = 3 } \\end{array}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "例3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "输入大小： (28, 31)；填充： 2；步幅： 3；滤波器大小： (5, 5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "\\begin{array} { l } { O H = \\frac { 28 + 2 \\cdot 2 - 5 } { 3 } + 1 = 10 } \\\\ { O W = \\frac { 31 + 2 \\cdot 2 - 5 } { 3 } + 1 = 11 } \\end{array}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如这些例子所示，通过在式（7.1）中代入值，就可以计算输出大小。  \n",
    "这里需要注意的是，虽然只要代入值就可以计算输出大小，但是所设定的值必须使式（7.1）中的$$\n",
    "\\frac { W + 2 P - F W } { S }\n",
    "$$和$$\n",
    "\\frac { H + 2 P - F H } { S }\n",
    "$$分别可以除尽。  \n",
    "当输出大小无法除尽时（结果是小数时），需要采取报错等对策。  \n",
    "顺便说一下，根据深度学习的框架的不同，当值无法除尽时，有时会向最接近的整数四舍五入，不进行\n",
    "报错而继续运行。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.2.5、3维数据的卷积运算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "之前的卷积运算的例子都是以有高、长方向的2维形状为对象的。  \n",
    "但是，图像是3维数据，除了高、长方向之外，还需要处理通道方向。  \n",
    "这里，我们按照与之前相同的顺序，看一下对加上了通道方向的3维数据进行卷积运算的例子。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图7-8是卷积运算的例子，图7-9是计算顺序。  \n",
    "这里以3通道的数据为例，展示了卷积运算的结果。  \n",
    "和2维数据时（图7-3的例子）相比，可以发现纵深方向（通道方向）上特征图增加了。  \n",
    "通道方向上有多个特征图时，会按通道进行输入数据和滤波器的卷积运算，并将结果相加，从而得到输出。 "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_8.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_9.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "需要注意的是，在3维数据的卷积运算中，输入数据和滤波器的通道数要设为相同的值。  \n",
    "在这个例子中，输入数据和滤波器的通道数一致，均为3。  \n",
    "滤波器大小可以设定为任意值（不过，每个通道的滤波器大小要全部相同）。  \n",
    "这个例子中滤波器大小为(3, 3)，但也可以设定为(2, 2)、 (1, 1)、 (5, 5)等任意值。  \n",
    "再强调一下，通道数只能设定为和输入数据的通道数相同的值（本例中为3）。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.2.6、结合方块思考"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "将数据和滤波器结合长方体的方块来考虑， 3维数据的卷积运算会很容易理解。  \n",
    "方块是如图7-10所示的3维长方体。  \n",
    "把3维数据表示为多维数组时，书写顺序为（channel, height, width）。  \n",
    "比如，通道数为 C、高度为H、长度为W的数据的形状可以写成（C, H, W）。  \n",
    "滤波器也一样，要按（channel,height, width）的顺序书写。  \n",
    "比如，通道数为C、滤波器高度为FH（Filter Height）、长度为FW（Filter Width）时，可以写成（C, FH, FW）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_10.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这个例子中，数据输出是1张特征图。  \n",
    "所谓1张特征图，换句话说，就是通道数为1的特征图。  \n",
    "那么，如果要在通道方向上也拥有多个卷积运算的输出，该怎么做呢？  \n",
    "为此，就需要用到多个滤波器（权重）。  \n",
    "用图表示的话，如图7-11所示。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_11.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图7-11中，通过应用FN个滤波器，输出特征图也生成了FN个。  \n",
    "如果将这FN个特征图汇集在一起，就得到了形状为(FN, OH, OW)的方块。  \n",
    "将这个方块传给下一层，就是CNN的处理流。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如图 7-11 所示，关于卷积运算的滤波器，也必须考虑滤波器的数量。  \n",
    "因此，作为4维数据，滤波器的权重数据要按(output_channel, input_channel, height, width)的顺序书写。  \n",
    "比如，通道数为3、大小为5 × 5的滤波器有20个时，可以写成(20, 3, 5, 5)。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "卷积运算中（和全连接层一样）存在偏置。   \n",
    "在图7-11的例子中，如果进一步追加偏置的加法运算处理，则结果如下面的图7-12所示。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图7-12中，每个通道只有一个偏置。  \n",
    "这里，偏置的形状是(FN, 1, 1)，滤波器的输出结果的形状是(FN, OH, OW)。这两个方块相加时，要对滤波器的输出结果(FN, OH, OW)按通道加上相同的偏置值。  \n",
    "另外，不同形状的方块相加时，可以基于NumPy的广播功能轻松实现（1.5.5节）。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_12.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.2.7、批处理"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "神经网络的处理中进行了将输入数据打包的批处理。  \n",
    "之前的全连接神经网络的实现也对应了批处理，通过批处理，能够实现处理的高效化和学习时对mini-batch的对应。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们希望卷积运算也同样对应批处理。  \n",
    "为此，需要将在各层间传递的数据保存为4维数据。  \n",
    "具体地讲，就是按(batch_num, channel, height, width)的顺序保存数据。  \n",
    "比如，将图7-12中的处理改成对N个数据进行批处理时，数据的形状如图7-13所示。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图7-13的批处理版的数据流中，在各个数据的开头添加了批用的维度。  \n",
    "像这样，数据作为4维的形状在各层间传递。  \n",
    "这里需要注意的是，网络间传递的是4维数据，对这N个数据进行了卷积运算。也就是说，批处理将N次的处理汇总成了1次进行。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_13.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.3、池化层"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "池化是缩小高、长方向上的空间的运算。  \n",
    "比如，如图7-14所示，进行将2 × 2的区域集约成1个元素的处理，缩小空间大小。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_14.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图7-14的例子是按步幅2进行2 × 2的Max池化时的处理顺序。  \n",
    "“Max池化”是获取最大值的运算，“2 × 2”表示目标区域的大小。  \n",
    "如图所示，从2 × 2的区域中取出最大的元素。  \n",
    "此外，这个例子中将步幅设为了2，所以 2 × 2的窗口的移动间隔为2个元素。  \n",
    "另外，一般来说，池化的窗口大小会和步幅设定成相同的值。  \n",
    "比如， 3 × 3的窗口的步幅会设为3， 4 × 4的窗口的步幅会设为4等。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "除了Max池化之外，还有Average池化等。  \n",
    "相对于Max池化是从目标区域中取出最大值，Average池化则是计算目标区域的平均值。  \n",
    "在图像识别领域，主要使用Max池化。  \n",
    "因此，本书中说到“池化层”时，指的是Max池化。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.3.1、池化层的特征"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 一、没有要学习的参数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "池化层和卷积层不同，没有要学习的参数。  \n",
    "池化只是从目标区域中取最大值（或者平均值），所以不存在要学习的参数。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 二、通道数不发生变化"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "经过池化运算，输入数据和输出数据的通道数不会发生变化。  \n",
    "如图7-15所示，计算是按通道独立进行的。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_15.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 三、对微小的位置变化具有鲁棒性（健壮）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "输入数据发生微小偏差时，池化仍会返回相同的结果。  \n",
    "因此，池化对输入数据的微小偏差具有鲁棒性。  \n",
    "比如， 3 × 3的池化的情况下，如图7-16所示，池化会吸收输入数据的偏差（根据数据的不同，结果有可能不一致）。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_16.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.4、卷积层和池化层的实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "前面我们详细介绍了卷积层和池化层，本节我们就用Python来实现这两个层。  \n",
    "和第5章一样，也给进行实现的类赋予forward和backward方法，并使其可以作为模块使用。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "大家可能会感觉卷积层和池化层的实现很复杂，但实际上，通过使用某种技巧，就可以很轻松地实现。  \n",
    "本节将介绍这种技巧，将问题简化，然后再进行卷积层的实现。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.4.1、4维数组"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如前所述， CNN中各层间传递的数据是4维数据。  \n",
    "所谓4维数据，比如数据的形状是(10, 1, 28, 28)，则它对应10个高为28、长为28、通道为1的数据。  \n",
    "用Python来实现的话，如下所示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"all\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(10, 1, 28, 28)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.random.rand(10, 1, 28, 28) # 随机生成数据\n",
    "x.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里，如果要访问第1个数据，只要写 x[0]就可以了（注意Python的索引是从0开始的）。  \n",
    "同样地，用x[1]可以访问第2个数据。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 28, 28)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "(1, 28, 28)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x[0].shape # (1, 28, 28)\n",
    "x[1].shape # (1, 28, 28)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果要访问第1个数据的第1个通道的空间数据，可以写成下面这样"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.36114598, 0.97789329, 0.2306671 , 0.79400863, 0.76689859,\n",
       "        0.27996329, 0.60198753, 0.99278945, 0.7754768 , 0.82630169,\n",
       "        0.05638491, 0.2166241 , 0.83370141, 0.74701016, 0.31962537,\n",
       "        0.30306497, 0.84693378, 0.47558095, 0.60786869, 0.64143842,\n",
       "        0.88827375, 0.48781315, 0.91964952, 0.47179797, 0.66312046,\n",
       "        0.89810026, 0.57286645, 0.10654236],\n",
       "       [0.94578108, 0.90686198, 0.15568656, 0.06177281, 0.81015032,\n",
       "        0.56729732, 0.33839131, 0.35361756, 0.90045204, 0.79878822,\n",
       "        0.25730785, 0.99737513, 0.30262539, 0.05697841, 0.38461109,\n",
       "        0.850614  , 0.2182969 , 0.33803186, 0.61309861, 0.01422203,\n",
       "        0.45510699, 0.4555518 , 0.17629379, 0.97797932, 0.69353211,\n",
       "        0.89185352, 0.67133466, 0.19831941],\n",
       "       [0.06559757, 0.89161926, 0.91909021, 0.08823087, 0.31742138,\n",
       "        0.95265915, 0.79627986, 0.20646638, 0.53004031, 0.20707162,\n",
       "        0.10276435, 0.77891134, 0.86035341, 0.92043846, 0.10128687,\n",
       "        0.39253241, 0.38300877, 0.38989989, 0.39154019, 0.48558868,\n",
       "        0.35488703, 0.98223913, 0.331972  , 0.19549325, 0.65602289,\n",
       "        0.11034584, 0.62360422, 0.30343509],\n",
       "       [0.56913995, 0.16251574, 0.78894803, 0.41622511, 0.21801566,\n",
       "        0.39014586, 0.58116355, 0.28909607, 0.48729029, 0.78827886,\n",
       "        0.95251591, 0.97635708, 0.33564358, 0.42729513, 0.43806786,\n",
       "        0.33434467, 0.41227018, 0.97082629, 0.63106173, 0.50597513,\n",
       "        0.51660807, 0.24907457, 0.38836273, 0.36159724, 0.02904112,\n",
       "        0.72116129, 0.75247848, 0.53502658],\n",
       "       [0.36086414, 0.30028365, 0.43059544, 0.32132191, 0.41576642,\n",
       "        0.43647104, 0.2853385 , 0.92192752, 0.41985022, 0.70167391,\n",
       "        0.06696619, 0.72615813, 0.44065966, 0.62262907, 0.81158005,\n",
       "        0.84265562, 0.99687666, 0.48735954, 0.98109755, 0.77799899,\n",
       "        0.32037458, 0.18534265, 0.68489071, 0.3887892 , 0.21191266,\n",
       "        0.40391291, 0.13813926, 0.42679978],\n",
       "       [0.13949221, 0.7806004 , 0.94492641, 0.93481481, 0.79247865,\n",
       "        0.83598009, 0.34134731, 0.5733675 , 0.40452163, 0.7537077 ,\n",
       "        0.48721058, 0.20891055, 0.07718814, 0.7759578 , 0.92733535,\n",
       "        0.10915909, 0.88463692, 0.06010445, 0.39044233, 0.03128259,\n",
       "        0.53339418, 0.61404635, 0.95427278, 0.8799603 , 0.02626162,\n",
       "        0.62194939, 0.98950739, 0.43394258],\n",
       "       [0.12169211, 0.38330962, 0.80776822, 0.65703658, 0.29254972,\n",
       "        0.44847169, 0.75350642, 0.13149514, 0.72040532, 0.75247686,\n",
       "        0.67702469, 0.492045  , 0.14604576, 0.88451685, 0.6281052 ,\n",
       "        0.76230447, 0.52355156, 0.34022426, 0.8657977 , 0.993546  ,\n",
       "        0.29745525, 0.80059165, 0.90579281, 0.22265434, 0.61119718,\n",
       "        0.5795482 , 0.10546896, 0.02023707],\n",
       "       [0.87918738, 0.20993959, 0.17452274, 0.10331266, 0.96642264,\n",
       "        0.89108384, 0.87379967, 0.25857699, 0.5977082 , 0.76433347,\n",
       "        0.89785663, 0.74918043, 0.18078982, 0.57771856, 0.64231337,\n",
       "        0.57360346, 0.61861769, 0.11344973, 0.038683  , 0.09342584,\n",
       "        0.04270292, 0.07178098, 0.30112389, 0.88943619, 0.17410292,\n",
       "        0.87774776, 0.49975422, 0.45259651],\n",
       "       [0.07715587, 0.97984338, 0.55569046, 0.66766202, 0.05265781,\n",
       "        0.62474376, 0.36721904, 0.92866316, 0.7300449 , 0.86748144,\n",
       "        0.52956616, 0.11494658, 0.40852145, 0.39117556, 0.23579756,\n",
       "        0.5294059 , 0.19648038, 0.33684458, 0.58595097, 0.71634541,\n",
       "        0.29479151, 0.21243276, 0.20677596, 0.7157385 , 0.38164084,\n",
       "        0.01606729, 0.6054594 , 0.13449168],\n",
       "       [0.97516283, 0.59067271, 0.17888099, 0.99049659, 0.11353258,\n",
       "        0.14575272, 0.90533087, 0.40520342, 0.70119223, 0.85934647,\n",
       "        0.77634026, 0.79660676, 0.37582255, 0.73253693, 0.17237632,\n",
       "        0.12501049, 0.00949777, 0.76058981, 0.28013403, 0.14305055,\n",
       "        0.17325425, 0.01714302, 0.51503282, 0.97124359, 0.83738007,\n",
       "        0.11915555, 0.51025598, 0.47342609],\n",
       "       [0.07286558, 0.57725377, 0.7394821 , 0.65327391, 0.96321951,\n",
       "        0.71864799, 0.19740375, 0.08894969, 0.92662399, 0.22161799,\n",
       "        0.58295574, 0.48968437, 0.50561017, 0.58212207, 0.12758741,\n",
       "        0.73581769, 0.49722937, 0.64467961, 0.10556178, 0.73762711,\n",
       "        0.31300217, 0.69121654, 0.50117657, 0.87087133, 0.14042989,\n",
       "        0.92601073, 0.64444689, 0.85585751],\n",
       "       [0.72541856, 0.96596634, 0.54241868, 0.02406634, 0.90532983,\n",
       "        0.79220832, 0.46470645, 0.76851571, 0.67059551, 0.58989145,\n",
       "        0.61699874, 0.89764889, 0.71247   , 0.90151631, 0.49336836,\n",
       "        0.33346398, 0.41437789, 0.04623371, 0.48015727, 0.25934804,\n",
       "        0.2072777 , 0.36512472, 0.154294  , 0.62272606, 0.81553392,\n",
       "        0.73238793, 0.19042166, 0.44449735],\n",
       "       [0.38314113, 0.41008846, 0.28437502, 0.71889751, 0.3887527 ,\n",
       "        0.12182918, 0.26898639, 0.20079443, 0.3920664 , 0.26642937,\n",
       "        0.48126514, 0.62045743, 0.43730053, 0.85653713, 0.62937582,\n",
       "        0.93985716, 0.25226727, 0.05857632, 0.58952962, 0.55034915,\n",
       "        0.75554223, 0.62103308, 0.44923463, 0.77928103, 0.02636197,\n",
       "        0.63363285, 0.20777415, 0.19058159],\n",
       "       [0.61806645, 0.60142569, 0.20468359, 0.21632613, 0.35494556,\n",
       "        0.27304579, 0.824064  , 0.51124684, 0.9663121 , 0.35356736,\n",
       "        0.66611926, 0.42088467, 0.75609595, 0.38402582, 0.76749493,\n",
       "        0.11385213, 0.68754286, 0.85629041, 0.09674273, 0.40075273,\n",
       "        0.77022777, 0.24737222, 0.02252036, 0.22727836, 0.60007302,\n",
       "        0.02728226, 0.14289476, 0.12118657],\n",
       "       [0.83095757, 0.06490326, 0.61693874, 0.53012284, 0.2182858 ,\n",
       "        0.63313485, 0.43584857, 0.49364685, 0.17954068, 0.69259751,\n",
       "        0.8292475 , 0.70792539, 0.91784821, 0.96840279, 0.92046001,\n",
       "        0.68334217, 0.74682382, 0.94569506, 0.71835784, 0.86205527,\n",
       "        0.46190854, 0.33351327, 0.04589359, 0.04444597, 0.87586097,\n",
       "        0.83232256, 0.14167438, 0.64787935],\n",
       "       [0.5428247 , 0.34910244, 0.01898459, 0.2155836 , 0.12289583,\n",
       "        0.22949394, 0.84217341, 0.05001692, 0.11511527, 0.33484292,\n",
       "        0.4806912 , 0.7228175 , 0.4219067 , 0.94654007, 0.78165014,\n",
       "        0.83859366, 0.97863092, 0.83753458, 0.15290633, 0.9639639 ,\n",
       "        0.95511563, 0.28986379, 0.44597672, 0.15460946, 0.32103942,\n",
       "        0.87298205, 0.55579621, 0.47041796],\n",
       "       [0.43927834, 0.40416375, 0.32305217, 0.02080306, 0.65366983,\n",
       "        0.25351028, 0.32220267, 0.8223982 , 0.27436348, 0.23330186,\n",
       "        0.85212843, 0.86412357, 0.71024369, 0.06617042, 0.49086591,\n",
       "        0.88728721, 0.48126029, 0.69215673, 0.36234567, 0.64071625,\n",
       "        0.89548814, 0.10209119, 0.55857736, 0.40190044, 0.84911925,\n",
       "        0.3743349 , 0.73555751, 0.99772934],\n",
       "       [0.54059991, 0.19278691, 0.37990216, 0.91254836, 0.19000272,\n",
       "        0.51171083, 0.51896423, 0.41426753, 0.34938341, 0.3924288 ,\n",
       "        0.90088468, 0.99066182, 0.40867727, 0.56248993, 0.78451755,\n",
       "        0.5897065 , 0.95509347, 0.24935142, 0.14073666, 0.37389943,\n",
       "        0.95859496, 0.48905871, 0.81367082, 0.89848173, 0.27101734,\n",
       "        0.84771774, 0.65892897, 0.07754315],\n",
       "       [0.03723443, 0.37986592, 0.66378827, 0.2273849 , 0.49741374,\n",
       "        0.09001667, 0.25420456, 0.63140746, 0.14029084, 0.31493955,\n",
       "        0.31679546, 0.12613707, 0.09872521, 0.37955801, 0.76763474,\n",
       "        0.62086516, 0.41672366, 0.65215821, 0.17731445, 0.92474315,\n",
       "        0.56947665, 0.52383629, 0.86487203, 0.88445766, 0.08555324,\n",
       "        0.04027797, 0.97226375, 0.90529088],\n",
       "       [0.10146487, 0.63175335, 0.2357796 , 0.19984474, 0.72713695,\n",
       "        0.19374473, 0.08954681, 0.64292036, 0.8298527 , 0.15787274,\n",
       "        0.00672084, 0.93692254, 0.34803368, 0.93923264, 0.59712181,\n",
       "        0.39847102, 0.25479125, 0.72024905, 0.60682405, 0.6076017 ,\n",
       "        0.86728304, 0.83787572, 0.68204832, 0.38185814, 0.63593253,\n",
       "        0.07345585, 0.96698429, 0.76554048],\n",
       "       [0.59649423, 0.8020146 , 0.95921481, 0.82988159, 0.93619657,\n",
       "        0.76286696, 0.69585334, 0.37577388, 0.01937197, 0.5009929 ,\n",
       "        0.27568611, 0.61485059, 0.56736566, 0.97620877, 0.72628438,\n",
       "        0.31378025, 0.97461131, 0.07889358, 0.56824495, 0.4379641 ,\n",
       "        0.72531967, 0.95491075, 0.34783862, 0.51370005, 0.08185199,\n",
       "        0.67001341, 0.24819143, 0.05289397],\n",
       "       [0.36125024, 0.989202  , 0.97744609, 0.59163897, 0.12289003,\n",
       "        0.13175109, 0.40809321, 0.89918715, 0.30616544, 0.6970249 ,\n",
       "        0.93190193, 0.18267082, 0.43131322, 0.95114264, 0.33581074,\n",
       "        0.7134177 , 0.55594134, 0.55506208, 0.75123343, 0.75988881,\n",
       "        0.50297652, 0.93627965, 0.17563985, 0.99028219, 0.77476316,\n",
       "        0.03766304, 0.21618764, 0.98303172],\n",
       "       [0.47990678, 0.55247086, 0.98549342, 0.63866706, 0.45924529,\n",
       "        0.51653089, 0.03113782, 0.77267833, 0.3026437 , 0.63228718,\n",
       "        0.69554524, 0.59283624, 0.08455028, 0.74836697, 0.90104336,\n",
       "        0.76976364, 0.17953779, 0.48010845, 0.95804049, 0.37783305,\n",
       "        0.05853505, 0.20320215, 0.63361503, 0.12624926, 0.16068004,\n",
       "        0.6580703 , 0.5249196 , 0.27021284],\n",
       "       [0.17132386, 0.7157896 , 0.46637633, 0.21573146, 0.60961296,\n",
       "        0.36691958, 0.91619985, 0.42741775, 0.94482648, 0.94946222,\n",
       "        0.04415428, 0.82717205, 0.31980556, 0.54858136, 0.13760143,\n",
       "        0.10985202, 0.1464481 , 0.51127696, 0.95937203, 0.89744509,\n",
       "        0.56034351, 0.26406073, 0.72877371, 0.11435508, 0.05834992,\n",
       "        0.61017201, 0.12982428, 0.24810456],\n",
       "       [0.58187235, 0.66533713, 0.3110078 , 0.05885872, 0.52063437,\n",
       "        0.83893604, 0.85594384, 0.54692044, 0.03527465, 0.34854981,\n",
       "        0.26289096, 0.60226663, 0.80344881, 0.52129966, 0.48458361,\n",
       "        0.40573634, 0.16090287, 0.90674931, 0.2081428 , 0.38352003,\n",
       "        0.20417743, 0.80877185, 0.07794666, 0.71837664, 0.34763647,\n",
       "        0.95700891, 0.56481085, 0.84200736],\n",
       "       [0.26304056, 0.2314914 , 0.29246776, 0.22441683, 0.20198503,\n",
       "        0.96199847, 0.81652417, 0.62756621, 0.09201332, 0.15129669,\n",
       "        0.06185966, 0.4008602 , 0.29929002, 0.7893091 , 0.78792687,\n",
       "        0.51451063, 0.48583679, 0.2009818 , 0.35820185, 0.04041695,\n",
       "        0.58376727, 0.3376929 , 0.70868858, 0.71616883, 0.30141408,\n",
       "        0.87161529, 0.39915766, 0.44780635],\n",
       "       [0.80986527, 0.31095597, 0.22412438, 0.54788623, 0.95852771,\n",
       "        0.46656296, 0.2283066 , 0.2191225 , 0.82348212, 0.09563282,\n",
       "        0.99832377, 0.15565063, 0.77592331, 0.20346011, 0.5199844 ,\n",
       "        0.22507214, 0.2873391 , 0.46058177, 0.82844944, 0.18502324,\n",
       "        0.25226997, 0.16366649, 0.3464475 , 0.50035819, 0.14209997,\n",
       "        0.22831302, 0.05509573, 0.74066516],\n",
       "       [0.12764029, 0.24497348, 0.46818422, 0.55088484, 0.61220087,\n",
       "        0.32698542, 0.14291313, 0.17171318, 0.11775995, 0.15428539,\n",
       "        0.18047041, 0.06586434, 0.01101267, 0.45104748, 0.26823991,\n",
       "        0.56422413, 0.9703131 , 0.62589073, 0.32122995, 0.49561073,\n",
       "        0.48979253, 0.05734412, 0.89868901, 0.52365032, 0.66610049,\n",
       "        0.79250179, 0.94193006, 0.54582724]])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x[0, 0] # 或者x[0][0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "像这样， CNN中处理的是4维数据，因此卷积运算的实现看上去会很复杂，但是通过使用下面要介绍的im2col这个技巧，问题就会变得很简单。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.4.2、基于 im2col的展开"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果老老实实地实现卷积运算，估计要重复好几层的 for语句。  \n",
    "这样的实现有点麻烦，而且， NumPy中存在使用for语句后处理变慢的缺点（NumPy中，访问元素时最好不要用 for语句）。  \n",
    "这里，我们不使用 for语句，而是使用im2col这个便利的函数进行简单的实现。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "im2col是一个函数，将输入数据展开以适合滤波器（权重）。  \n",
    "如图7-17所示，对3维的输入数据应用im2col后，数据转换为2维矩阵（正确地讲，是把包含批数量的4维数据转换成了2维数据）。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_17.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "im2col会把输入数据展开以适合滤波器（权重）。  \n",
    "具体地说，如图7-18所示，对于输入数据，将应用滤波器的区域（3维方块）横向展开为1列。  \n",
    "im2col会在所有应用滤波器的地方进行这个展开处理。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_18.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在图7-18中，为了便于观察，将步幅设置得很大，以使滤波器的应用区域不重叠。  \n",
    "而在实际的卷积运算中，滤波器的应用区域几乎都是重叠的。  \n",
    "在滤波器的应用区域重叠的情况下，使用im2col展开后，展开后的元素个数会多于原方块的元素个数。  \n",
    "因此，使用im2col的实现存在比普通的实现消耗更多内存的缺点。  \n",
    "但是，汇总成一个大的矩阵进行计算，对计算机的计算颇有益处。  \n",
    "比如，在矩阵计算的库（线性代数库）等中，矩阵计算的实现已被高度最优化，可以高速地进行大矩阵的乘法运算。  \n",
    "因此，通过归结到矩阵计算上，可以有效地利用线性代数库。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "im2col这个名称是“image to column”的缩写，翻译过来就是“从图像到矩阵”的意思。  \n",
    "Caffe、Chainer 等深度学习框架中有名为im2col的函数，并且在卷积层的实现中，都使用了 im2col。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用 im2col展开输入数据后，之后就只需将卷积层的滤波器（权重）纵向展开为1列，并计算2个矩阵的乘积即可（参照图7-19）。  \n",
    "这和全连接层的Affine层进行的处理基本相同。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如图7-19所示，基于 im2col方式的输出结果是2维矩阵。  \n",
    "因为CNN中数据会保存为4维数组，所以要将2维输出数据转换为合适的形状。  \n",
    "以上就是卷积层的实现流程。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_19.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.4.3、卷积层的实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "本书提供了im2col函数，并将这个im2col函数作为黑盒（不关心内部实现）使用。  \n",
    "im2col的实现内容在 common/util.py中，它的实现（实质上）是一个10行左右的简单函数。  \n",
    "有兴趣的读者可以参考。  \n",
    "im2col这一便捷函数具有以下接口。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "im2col (input_data, filter_h, filter_w, stride=1, pad=0)\n",
    "• input_data―由（数据量，通道，高，长）的4维数组构成的输入数据\n",
    "• filter_h―滤波器的高\n",
    "• filter_w―滤波器的长\n",
    "• stride―步幅\n",
    "• pad―填充\n",
    "'''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "im2col会考虑滤波器大小、 步幅、填充，将输入数据展开为2维数组。  \n",
    "现在，我们来实际使用一下这个im2col。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(9, 75)\n",
      "(90, 75)\n"
     ]
    }
   ],
   "source": [
    "import sys, os\n",
    "sys.path.append(os.pardir)\n",
    "from common.util import im2col\n",
    "\n",
    "x1 = np.random.rand(1, 3, 7, 7)\n",
    "col1 = im2col(x1, 5, 5, stride=1, pad=0)\n",
    "print(col1.shape) # (9, 75)\n",
    "\n",
    "x2 = np.random.rand(10, 3, 7, 7) # 10个数据\n",
    "col2 = im2col(x2, 5, 5, stride=1, pad=0)\n",
    "print(col2.shape) # (90, 75)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里举了两个例子。  \n",
    "第一个是批大小为1、通道为3的7 × 7的数据，第二个的批大小为10，数据形状和第一个相同。分别对其应用im2col函数，在这两种情形下，第2维的元素个数均为75。  \n",
    "这是滤波器（通道为3、大小为5 × 5）的元素个数的总和。  \n",
    "批大小为1时， im2col的结果是 (9, 75)。而第2个例子中批大小为10，所以保存了10倍的数据，即(90, 75)。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在使用im2col来实现卷积层。  \n",
    "这里我们将卷积层实现为名为Convolution的类。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Convolution:\n",
    "    def __init__(self, W, b, stride=1, pad=0):\n",
    "        self.W = W\n",
    "        self.b = b\n",
    "        self.stride = stride\n",
    "        self.pad = pad\n",
    "    def forward(self, x):\n",
    "        FN, C, FH, FW = self.W.shape\n",
    "        N, C, H, W = x.shape\n",
    "        out_h = int(1 + (H + 2*self.pad - FH) / self.stride)\n",
    "        out_w = int(1 + (W + 2*self.pad - FW) / self.stride)\n",
    "        \n",
    "        col = im2col(x, FH, FW, self.stride, self.pad)\n",
    "        col_W = self.W.reshape(FN, -1).T # 滤波器的展开\n",
    "        out = np.dot(col, col_W) + self.b\n",
    "        \n",
    "        out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)\n",
    "        \n",
    "        return out"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "卷积层的初始化方法将滤波器（权重）、偏置、步幅、填充作为参数接收。  \n",
    "滤波器是 (FN, C, FH, FW)的 4 维形状。  \n",
    "另外， FN、 C、 FH、 FW分别是 Filter Number（滤波器数量）、 Channel、 Filter Height、 Filter Width的缩写。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里用粗体字表示Convolution层的实现中的重要部分。  \n",
    "在这些粗体字部分，用im2col展开输入数据，并用reshape将滤波器展开为2维数组。  \n",
    "然后，计算展开后的矩阵的乘积。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "展开滤波器的部分（代码段中的粗体字）如图7-19所示，将各个滤波器的方块纵向展开为1列。  \n",
    "这里通过 reshape(FN,-1)将参数指定为 -1，这是reshape的一个便利的功能。  \n",
    "通过在 reshape时指定为 -1， reshape函数会自动计算 -1维度上的元素个数，以使多维数组的元素个数前后一致。  \n",
    "比如，(10, 3, 5, 5)形状的数组的元素个数共有750个，指定 reshape(10,-1)后，就\n",
    "会转换成(10, 75)形状的数组。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "forward的实现中，最后会将输出大小转换为合适的形状。  \n",
    "转换时使用了NumPy的transpose函数。   \n",
    "transpose会更改多维数组的轴的顺序。  \n",
    "如图7-20所示，通过指定从0开始的索引（编号）序列，就可以更改轴的顺序。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_20.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上就是卷积层的 forward处理的实现。  \n",
    "通过使用 im2col进行展开，基本上可以像实现全连接层的Affine层一样来实现（5.6节）。  \n",
    "接下来是卷积层的反向传播的实现，因为和Affine层的实现有很多共通的地方，所以就不再介绍了。  \n",
    "但有一点需要注意，在进行卷积层的反向传播时，必须进行im2col的逆处理。  \n",
    "这可以使用本书提供的col2im函数（col2im的实现在common/util.py中）来进行。  \n",
    "除了使用 col2im这一点，卷积层的反向传播和Affine层的实现方式都一样。  \n",
    "卷积层的反向传播的实现在common/layer.py中，有兴趣的读者可以参考。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.4.4、池化层的实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "池化层的实现和卷积层相同，都使用im2col展开输入数据。  \n",
    "不过，池化的情况下，在通道方向上是独立的，这一点和卷积层不同。  \n",
    "具体地讲，如图7-21所示，池化的应用区域按通道单独展开。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_21.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "像这样展开之后，只需对展开的矩阵求各行的最大值，并转换为合适的形状即可（图7-22）。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_22.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面就是池化层的forward处理的实现流程。  \n",
    "下面来看一下Python的实现示例。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Pooling:\n",
    "    def __init__(self, pool_h, pool_w, stride=1, pad=0):\n",
    "        self.pool_h = pool_h\n",
    "        self.pool_w = pool_w\n",
    "        self.stride = stride\n",
    "        self.pad = pad\n",
    "        \n",
    "    def forward(self, x):\n",
    "        N, C, H, W = x.shape\n",
    "        out_h = int(1 + (H - self.pool_h) / self.stride)\n",
    "        out_w = int(1 + (W - self.pool_w) / self.stride)\n",
    "        \n",
    "        # 展开(1)\n",
    "        col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)\n",
    "        col = col.reshape(-1, self.pool_h*self.pool_w)\n",
    "        \n",
    "        # 最大值(2)\n",
    "        out = np.max(col, axis=1)\n",
    "        # 转换(3)\n",
    "        out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)\n",
    "        \n",
    "        return out"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如图7-22所示，池化层的实现按下面3个阶段进行。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1.展开输入数据。  \n",
    "2.求各行的最大值。  \n",
    "3.转换为合适的输出大小。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "各阶段的实现都很简单，只有一两行代码。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最大值的计算可以使用 NumPy 的 np.max 方法。   \n",
    "np.max 可以指定axis参数，并在这个参数指定的各个轴方向上求最大值。  \n",
    "比如，如果写成 np.max(x, axis=1)，就可以在输入 x的第 1 维的各个轴方向上求最大值。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上就是池化层的 forward处理的介绍。  \n",
    "如上所述，通过将输入数据展开为容易进行池化的形状，后面的实现就会变得非常简单。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "关于池化层的backward处理，之前已经介绍过相关内容，这里就不再介绍了。  \n",
    "另外，池化层的backward处理可以参考ReLU层的实现中使用的max的反向传播（5.5.1节）。  \n",
    "池化层的实现在common/layer.py中，有兴趣的读者可以参考。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.5、CNN的实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们已经实现了卷积层和池化层，现在来组合这些层，搭建进行手写数字识别的CNN。  \n",
    "这里要实现如图7-23所示的CNN。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_23.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如图7-23所示，网络的构成是“Convolution - ReLU - Pooling -Affine -\n",
    "ReLU - Affine - Softmax”，我们将它实现为名为SimpleConvNet的类。  \n",
    "首先来看一下SimpleConvNet的初始化（__init__），取下面这些参数。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "参数\n",
    "• input_dim―输入数据的维度：（ 通道，高，长）\n",
    "• conv_param―卷积层的超参数（字典）。字典的关键字如下：\n",
    "    filter_num―滤波器的数量\n",
    "    filter_size―滤波器的大小\n",
    "    stride―步幅\n",
    "    pad―填充\n",
    "• hidden_size―隐藏层（全连接）的神经元数量\n",
    "• output_size―输出层（全连接）的神经元数量\n",
    "• weitght_int_std―初始化时权重的标准差\n",
    "'''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里，卷积层的超参数通过名为 conv_param的字典传入。  \n",
    "我们设想它会像 {'filter_num':30,'filter_size':5, 'pad':0, 'stride':1}这样，保存必要的超参数值。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "SimpleConvNet的初始化的实现稍长，我们分成3部分来说明，首先是初始化的最开始部分。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "class SimpleConvNet:\n",
    "    def __init__(self, input_dim=(1, 28, 28),conv_param={'filter_num':30, 'filter_size':5,'pad':0, 'stride':1},hidden_size=100, output_size=10, weight_init_std=0.01):\n",
    "        filter_num = conv_param['filter_num']\n",
    "        filter_size = conv_param['filter_size']\n",
    "        filter_pad = conv_param['pad']\n",
    "        filter_stride = conv_param['stride']\n",
    "        input_size = input_dim[1]\n",
    "        conv_output_size = (input_size - filter_size + 2*filter_pad) / filter_stride + 1\n",
    "        pool_output_size = int(filter_num * (conv_output_size/2) * (conv_output_size/2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里将由初始化参数传入的卷积层的超参数从字典中取了出来（以方便后面使用），然后，计算卷积层的输出大小。  \n",
    "接下来是权重参数的初始化部分。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "self.params = {}\n",
    "self.params['W1'] = weight_init_std * np.random.randn(filter_num, input_dim[0],filter_size, filter_size)\n",
    "self.params['b1'] = np.zeros(filter_num)\n",
    "self.params['W2'] = weight_init_std * np.random.randn(pool_output_size,hidden_size)\n",
    "self.params['b2'] = np.zeros(hidden_size)\n",
    "self.params['W3'] = weight_init_std * np.random.randn(hidden_size, output_size)\n",
    "self.params['b3'] = np.zeros(output_size)\n",
    "'''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "学习所需的参数是第1层的卷积层和剩余两个全连接层的权重和偏置。  \n",
    "将这些参数保存在实例变量的 params字典中。将第1层的卷积层的权重设为关键字W1，偏置设为关键字b1。  \n",
    "同样，分别用关键字W2、 b2和关键字W3、 b3来保存第2个和第3个全连接层的权重和偏置。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，生成必要的层。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "self.layers = OrderedDict()\n",
    "self.layers['Conv1'] = Convolution(self.params['W1'],self.params['b1'],conv_param['stride'],conv_param['pad'])\n",
    "self.layers['Relu1'] = Relu()\n",
    "self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)\n",
    "self.layers['Affine1'] = Affine(self.params['W2'],self.params['b2'])\n",
    "self.layers['Relu2'] = Relu()\n",
    "self.layers['Affine2'] = Affine(self.params['W3'],self.params['b3'])\n",
    "self.last_layer = softmaxwithloss()\n",
    "'''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从最前面开始按顺序向有序字典（OrderedDict）的 layers中添加层。  \n",
    "只有最后的SoftmaxWithLoss层被添加到别的变量lastLayer中。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上就是 SimpleConvNet的初始化中进行的处理。  \n",
    "像这样初始化后，进行推理的predict方法和求损失函数值的loss方法就可以像下面这样实现。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def predict(self, x):\n",
    "    for layer in self.layers.values():\n",
    "        x = layer.forward(x)\n",
    "    return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def loss(self, x, t):\n",
    "    y = self.predict(x)\n",
    "    return self.lastLayer.forward(y, t)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里，参数 x是输入数据， t是教师标签。  \n",
    "用于推理的 predict方法从头开始依次调用已添加的层，并将结果传递给下一层。  \n",
    "在求损失函数的 loss方法中，除了使用 predict方法进行的 forward处理之外，还会继续进行forward处理，直到到达最后的SoftmaxWithLoss层。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来是基于误差反向传播法求梯度的代码实现。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "def gradient(self, x, t):\n",
    "    # forward\n",
    "    self.loss(x, t)\n",
    "    # backward\n",
    "    dout = 1\n",
    "    dout = self.lastLayer.backward(dout)\n",
    "    layers = list(self.layers.values())\n",
    "    layers.reverse()\n",
    "    for layer in layers:\n",
    "        dout = layer.backward(dout)\n",
    "    # 设定\n",
    "    grads = {}\n",
    "    grads['W1'] = self.layers['Conv1'].dW\n",
    "    grads['b1'] = self.layers['Conv1'].db\n",
    "    grads['W2'] = self.layers['Affine1'].dW\n",
    "    grads['b2'] = self.layers['Affine1'].db\n",
    "    grads['W3'] = self.layers['Affine2'].dW\n",
    "    grads['b3'] = self.layers['Affine2'].db\n",
    "    return grads"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "参数的梯度通过误差反向传播法（反向传播）求出，通过把正向传播和反向传播组装在一起来完成。  \n",
    "因为已经在各层正确实现了正向传播和反向传播的功能，所以这里只需要以合适的顺序调用即可。  \n",
    "最后，把各个权重参数的梯度保存到grads字典中。  \n",
    "这就是SimpleConvNet的实现。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，使用这个 SimpleConvNet学习MNIST数据集。  \n",
    "用于学习的代码与4.5节中介绍的代码基本相同，因此这里不再罗列（源代码在 ch07/train_convnet.py中）。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果使用MNIST数据集训练 SimpleConvNet，则训练数据的识别率为99.82%，测试数据的识别率为98.96%（每次学习的识别精度都会发生一些误差）。  \n",
    "测试数据的识别率大约为99%，就小型网络来说，这是一个非常高的识别率。  \n",
    "下一章，我们会通过进一步叠加层来加深网络，实现测试数据的识别率超过99%的网络。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如上所述，卷积层和池化层是图像识别中必备的模块。   \n",
    "CNN可以有效读取图像中的某种特性，在手写数字识别中，还可以实现高精度的识别。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.6、CNN的可视化"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "CNN中用到的卷积层在“观察”什么呢？  \n",
    "本节将通过卷积层的可视化，探索CNN中到底进行了什么处理。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.6.1、第1层权重的可视化"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "刚才我们对MNIST数据集进行了简单的CNN学习。  \n",
    "当时，第1层的卷积层的权重的形状是(30, 1, 5, 5)，即30个大小为5 × 5、通道为1的滤波器。  \n",
    "滤波器大小是5 × 5、通道数是1，意味着滤波器可以可视化为1通道的灰度图像。  \n",
    "现在，我们将卷积层（第1层）的滤波器显示为图像。  \n",
    "这里，我们来比较一下学习前和学习后的权重，结果如图7-24所示（源代码在 ch07/visualize_filter.py中）。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图7-24中，学习前的滤波器是随机进行初始化的，所以在黑白的浓淡上没有规律可循，但学习后的滤波器变成了有规律的图像。  \n",
    "我们发现，通过学习，滤波器被更新成了有规律的滤波器，比如从白到黑渐变的滤波器、含有块状区域（称为blob）的滤波器等。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_24.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果要问图7-24中右边的有规律的滤波器在“观察”什么，答案就是它在观察边缘（颜色变化的分界线）和斑块（局部的块状区域）等。  \n",
    "比如，左半部分为白色、右半部分为黑色的滤波器的情况下，如图7-25所示，会对垂直方向上的边缘有响应。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_25.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图7-25中显示了选择两个学习完的滤波器对输入图像进行卷积处理时的结果。  \n",
    "我们发现“滤波器1”对垂直方向上的边缘有响应，“滤波器2”对水平方向上的边缘有响应。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由此可知，卷积层的滤波器会提取边缘或斑块等原始信息。  \n",
    "而刚才实现的CNN会将这些原始信息传递给后面的层。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.6.2、基于分层结构的信息提取"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的结果是针对第1层的卷积层得出的。  \n",
    "第1层的卷积层中提取了边缘或斑块等“低级”信息，那么在堆叠了多层的CNN中，各层中又会提取什么样的信息呢？  \n",
    "根据深度学习的可视化相关的研究，随着层次加深，提取的信息（正确地讲，是反映强烈的神经元）也越来越抽象。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图7-26中展示了进行一般物体识别（车或狗等）的8层CNN。  \n",
    "这个网络结构的名称是下一节要介绍的AlexNet。   \n",
    "AlexNet网络结构堆叠了多层卷积层和池化层，最后经过全连接层输出结果。  \n",
    "图7-26的方块表示的是中间数据，对于这些中间数据，会连续应用卷积运算。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_26.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如图7-26所示，如果堆叠了多层卷积层，则随着层次加深，提取的信息也愈加复杂、抽象，这是深度学习中很有意思的一个地方。  \n",
    "最开始的层对简单的边缘有响应，接下来的层对纹理有响应，再后面的层对更加复杂的物体部件有响应。  \n",
    "也就是说，随着层次加深，神经元从简单的形状向“高级”信息变化。  \n",
    "换句话说，就像我们理解东西的“含义”一样，响应的对象在逐渐变化。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.7、具有代表性的CNN"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "关于CNN，迄今为止已经提出了各种网络结构。  \n",
    "这里，我们介绍其中特别重要的两个网络，一个是在1998年首次被提出的CNN元祖LeNet，另一个是在深度学习受到关注的2012年被提出的AlexNet。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.7.1、LeNet"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "LeNet在1998年被提出，是进行手写数字识别的网络。  \n",
    "如图7-27所示，它有连续的卷积层和池化层（正确地讲，是只“抽选元素”的子采样层），最后经全连接层输出结果。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_27.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "和“现在的CNN”相比， LeNet有几个不同点。  \n",
    "第一个不同点在于激活函数。 LeNet中使用sigmoid函数，而现在的CNN中主要使用ReLU函数。  \n",
    "此外，原始的LeNet中使用子采样（subsampling）缩小中间数据的大小，而现在的CNN中Max池化是主流。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "综上， LeNet与现在的CNN虽然有些许不同，但差别并不是那么大。  \n",
    "想到LeNet是20多年前提出的最早的CNN，还是很令人称奇的。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.7.2、AlexNet"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在LeNet问世20多年后， AlexNet被发布出来。   \n",
    "AlexNet是引发深度学习热潮的导火线，不过它的网络结构和LeNet基本上没有什么不同，如图7-28所示。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"./imgs/7_28.png\"></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "AlexNet叠有多个卷积层和池化层，最后经由全连接层输出结果。  \n",
    "虽然结构上AlexNet和LeNet没有大的不同，但有以下几点差异。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "• 激活函数使用ReLU。  \n",
    "• 使用进行局部正规化的LRN（Local Response Normalization）层。  \n",
    "• 使用Dropout（6.4.3节）。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如上所述，关于网络结构， LeNet和AlexNet没有太大的不同。  \n",
    "但是，围绕它们的环境和计算机技术有了很大的进步。  \n",
    "具体地说，现在任何人都可以获得大量的数据。  \n",
    "而且，擅长大规模并行计算的GPU得到普及，高速进行大量的运算已经成为可能。  \n",
    "大数据和GPU已成为深度学习发展的巨大的原动力。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "大多数情况下，深度学习（加深了层次的网络）存在大量的参数。  \n",
    "因此，学习需要大量的计算，并且需要使那些参数“满意”的大量数据。  \n",
    "可以说是GPU和大数据给这些课题带来了希望。  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.8、小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "本章介绍了CNN。  \n",
    "构成CNN的基本模块的卷积层和池化层虽然有些复杂，但是一旦理解了，之后就只是如何使用它们的问题了。  \n",
    "本章为了使读者在实现层面上理解卷积层和池化层，花了不少时间进行介绍。  \n",
    "在图像处理领域，几乎毫无例外地都会使用CNN。  \n",
    "请扎实地理解本章的内容，然后进入最后一章的学习。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "1、CNN在此前的全连接层的网络中新增了卷积层和池化层。\n",
    "2、使用im2col函数可以简单、高效地实现卷积层和池化层。\n",
    "3、通过CNN的可视化，可知随着层次变深，提取的信息愈加高级。\n",
    "4、LeNet和AlexNet是CNN的代表性网络。\n",
    "5、在深度学习的发展中，大数据和GPU做出了很大的贡献。\n",
    "'''"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
